package com.simpleframe.util;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;

import org.apache.log4j.Logger;

import com.simpleframe.exception.SimpleframeException;

public abstract class ReflectUtil {

	private static Logger log = Logger.getLogger(ReflectUtil.class);

	/**
	 * 根据参数得到一个构造方法
	 * 
	 * @param <T>
	 * @param clazz
	 * @param parameterTypes
	 *            构造方法参数
	 * @return
	 */
	public static <T> Constructor<T> getConstructor(Class<T> clazz,
			Class<?>[] parameterTypes) {
		Constructor<T> constructor = null;
		try {
			constructor = clazz.getDeclaredConstructor(parameterTypes);

			if (log.isTraceEnabled())
				log.trace("found constructor " + clazz.getName() + "("
						+ Arrays.toString(parameterTypes) + ")");

		} catch (SecurityException e) {
			throw new SimpleframeException(
					"wasn't allowed to get constructor '" + clazz.getName()
							+ "(" + getParameterTypesText(parameterTypes)
							+ ")'", e);
		} catch (NoSuchMethodException e) {
			throw new SimpleframeException("couldn't find constructor '"
					+ clazz.getName() + "("
					+ getParameterTypesText(parameterTypes) + ")'", e);
		}

		return constructor;
	}

	/**
	 * 根据字段名称得到字段
	 * 
	 * @param clazz
	 * @param fieldName
	 * @return
	 */
	public static Field getField(Class<?> clazz, String fieldName) {
		return getField(clazz, fieldName, clazz);
	}

	private static Field getField(Class<?> clazz, String fieldName,
			Class<?> original) {
		Field field = null;

		try {
			field = clazz.getDeclaredField(fieldName);
			log.trace("found field " + fieldName + " in " + clazz.getName());
		} catch (SecurityException e) {
			throw new SimpleframeException("wasn't allowed to get field '"
					+ clazz.getName() + "." + fieldName + "'", e);
		} catch (NoSuchFieldException e) {
			if (clazz.getSuperclass() != null) {
				return getField(clazz.getSuperclass(), fieldName, original);
			} else {
				throw new SimpleframeException("couldn't find field '"
						+ original.getName() + "." + fieldName + "'", e);
			}
		}

		return field;
	}

	/**
	 * 根据方法名称得到方法
	 * 
	 * @param clazz
	 * @param methodName
	 * @param parameterTypes
	 * @return
	 */
	public static Method getMethod(Class<?> clazz, String methodName,
			Class<?>[] parameterTypes) {
		return getMethod(clazz, methodName, parameterTypes, clazz);
	}

	private static Method getMethod(Class<?> clazz, String methodName,
			Class<?>[] parameterTypes, Class<?> original) {
		Method method = null;

		try {
			method = clazz.getDeclaredMethod(methodName, parameterTypes);

			if (log.isTraceEnabled())
				log.trace("found method " + clazz.getName() + "." + methodName
						+ "(" + Arrays.toString(parameterTypes) + ")");

		} catch (SecurityException e) {
			throw new SimpleframeException("wasn't allowed to get method '"
					+ clazz.getName() + "." + methodName + "("
					+ getParameterTypesText(parameterTypes) + ")'", e);
		} catch (NoSuchMethodException e) {
			if (clazz.getSuperclass() != null) {
				return getMethod(clazz.getSuperclass(), methodName,
						parameterTypes, original);
			} else {
				throw new SimpleframeException("couldn't find method '"
						+ original.getName() + "." + methodName + "("
						+ getParameterTypesText(parameterTypes) + ")'", e);
			}
		}

		return method;
	}

	private static String getParameterTypesText(Class<?>[] parameterTypes) {
		if (parameterTypes == null)
			return "";
		StringBuilder parameterTypesText = new StringBuilder();
		for (int i = 0; i < parameterTypes.length; i++) {
			Class<?> parameterType = parameterTypes[i];
			parameterTypesText.append(parameterType.getName());
			if (i != parameterTypes.length - 1) {
				parameterTypesText.append(", ");
			}
		}
		return parameterTypesText.toString();
	}
	
	public static Object newInstance(String className){
		return newInstance(ClassUtil.forName(className));
	}

	public static <T> T newInstance(Class<T> clazz) {
		return newInstance(clazz, null, null);
	}

	public static <T> T newInstance(Constructor<T> constructor) {
		return newInstance(null, constructor, null);
	}

	public static <T> T newInstance(Constructor<T> constructor, Object[] args) {
		return newInstance(null, constructor, args);
	}

	private static <T> T newInstance(Class<T> clazz,
			Constructor<T> constructor, Object[] args) {
		if ((clazz == null) && (constructor == null)) {
			throw new IllegalArgumentException(
					"can't create new instance without clazz or constructor");
		}

		String className = null;
		try {
			log.trace("creating new instance for class '" + className
					+ "' with args " + Arrays.toString(args));
			if (constructor == null) {
				log.trace("getting default constructor");
				constructor = clazz.getConstructor((Class[]) null);
			}
			className = constructor.getDeclaringClass().getName();
			if (!constructor.isAccessible()) {
				log.trace("making constructor accessible");
				constructor.setAccessible(true);
			}
			return constructor.newInstance(args);

		} catch (Throwable t) {
			throw new SimpleframeException("couldn't construct new '"
					+ className + "' with args " + Arrays.toString(args), t);
		}
	}

	/**
	 * 得到字段的值
	 * 
	 * @param field
	 * @param object
	 * @return
	 */
	public static Object get(Field field, Object object) {
		Assert.notNull(field, "field is null");

		try {
			Object value = field.get(object);
			log.trace("got value '" + value + "' from field '"
					+ field.getName() + "'");
			return value;
		} catch (Exception e) {
			throw new SimpleframeException("couldn't get '" + field.getName()
					+ "'", e);
		}
	}

	/**
	 * 设置字段值
	 * 
	 * @param field
	 * @param object
	 * @param value
	 */
	public static void set(Field field, Object object, Object value) {
		Assert.notNull(field, "field is null");

		try {
			log.trace("setting field '" + field.getName() + "' to value '"
					+ value + "'");
			if (!field.isAccessible()) {
				log.trace("making field accessible");
				field.setAccessible(true);
			}
			field.set(object, value);
		} catch (Exception e) {
			throw new SimpleframeException("couldn't set '" + field.getName()
					+ "' to '" + value + "'", e);
		}
	}

	/**
	 * 运行方法
	 * 
	 * @param method
	 * @param target
	 * @param args
	 * @return
	 */
	public static Object invoke(Method method, Object target, Object[] args) {
		if (method == null) {
			throw new SimpleframeException("method is null");
		}
		try {
			log.trace("invoking '" + method.getName() + "' on '" + target
					+ "' with " + Arrays.toString(args));
			if (!method.isAccessible()) {
				log.trace("making method accessible");
				method.setAccessible(true);
			}
			return method.invoke(target, args);
		} catch (InvocationTargetException e) {
			Throwable targetException = e.getTargetException();
			throw new SimpleframeException("couldn't invoke '"
					+ method.getName() + "' with " + Arrays.toString(args)
					+ " on " + target + ": " + targetException.getMessage(),
					targetException);
		} catch (Exception e) {
			throw new SimpleframeException("couldn't invoke '"
					+ method.getName() + "' with " + Arrays.toString(args)
					+ " on " + target + ": " + e.getMessage(), e);
		}
	}

	/**
	 * 运行一个对象的get方法
	 * 
	 * @param target
	 *            目标对象
	 * @param fields
	 *            要执行的字段，可以为“xxxxx.xx.xxx” 意为：target.getXxxxx.getXx.getXxx();
	 * @return
	 */
	public static Object executeGet(Object target, String fields) {
		
		if(target==null){
			throw new SimpleframeException("target 不能为空");
		}
		
		if(fields==null){
			throw new SimpleframeException("fields 不能为空");
		}
		
		String[] fieldStrs = fields.split("\\.");
		
		for (int i=0;i<fieldStrs.length; i++) {
			
			if(target==null){
				throw new SimpleframeException(fieldStrs[i-1]+" 不能为空");
			}
			
			Method getMethod = getMethod(target.getClass(), toGetMethod(fieldStrs[i]),
					null);
			//log.debug(getMethod.toString());
			target = invoke(getMethod, target, null);
			//log.debug(target.toString());
		}
		return target;
	}

	private static String toGetMethod(String field) {
		StringBuffer sb = new StringBuffer("get");
		sb.append(field.substring(0, 1).toUpperCase());
		sb.append(field.substring(1));
		return sb.toString();
	}

	public static void main(String[] args) {
		X x = new X();
		x.setA("abc");
		Y y = new Y();
		y.setA("hhh");
		x.setY(y);
		System.out.println(executeGet(x, "y.a"));
	}
	
	public static class X {
		private String a;
		private Y y;
		
		public Y getY() {
			return y;
		}

		public void setY(Y y) {
			this.y = y;
		}

		public String getA() {
			return this.a;
		}
		
		public void setA(String a) {
			this.a = a;
		}
	}
	
	public static class Y {
		private String a;
		
		public String getA() {
			return this.a;
		}
		
		public void setA(String a) {
			this.a = a;
		}
	}

}
